package com.like.bluetooth.sample.ble

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.widget.doAfterTextChanged
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.like.bluetooth.ble.central.connect.executor.AbstractConnectExecutor
import com.like.bluetooth.ble.central.connect.executor.ConnectExecutorFactory
import com.like.bluetooth.exception.BluetoothException
import com.like.bluetooth.exception.BluetoothExceptionBusy
import com.like.bluetooth.exception.BluetoothExceptionCancelTimeout
import com.like.bluetooth.sample.BR
import com.like.bluetooth.sample.R
import com.like.bluetooth.sample.databinding.FragmentBleConnectBinding
import com.like.common.util.Logger
import com.like.recyclerview.layoutmanager.WrapLinearLayoutManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * 连接设备界面
 */
class BleConnectFragment : Fragment() {
    private lateinit var mBinding: FragmentBleConnectBinding
    private lateinit var mData: BleScanInfo
    private val connectExecutor: AbstractConnectExecutor by lazy {
        ConnectExecutorFactory.get(requireContext(), mData.address).apply {
            onBleOn = {
                context?.let {
                    if (mBinding.tvConnectStatus.text != "未连接") {
                        val blueColor = ContextCompat.getColor(it, R.color.ble_text_blue)
                        mBinding.tvConnectStatus.setTextColor(blueColor)
                        mBinding.tvConnectStatus.text = "蓝牙已打开"
                    }
                }
            }
            onBleOff = {
                context?.let {
                    val redColor = ContextCompat.getColor(it, R.color.ble_text_red)
                    mBinding.tvConnectStatus.setTextColor(redColor)
                    mBinding.tvConnectStatus.text = "蓝牙未打开"
                }
            }
        }
    }
    private val mAdapter: BleConnectAdapter by lazy { BleConnectAdapter(requireActivity(), connectExecutor) }

    companion object {
        fun newInstance(bleScanInfo: BleScanInfo?): BleConnectFragment {
            return BleConnectFragment().apply {
                arguments = Bundle().apply {
                    putSerializable("data", bleScanInfo)
                }
            }
        }
    }

    fun getBleScanInfo(): BleScanInfo? {
        return arguments?.getSerializable("data") as? BleScanInfo
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        mBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_ble_connect, container, true)
        mData = arguments?.getSerializable("data") as? BleScanInfo ?: throw UnsupportedOperationException("data is null")
        mBinding.setVariable(BR.bleScanInfo, mData)
        mBinding.rv.layoutManager = WrapLinearLayoutManager(requireContext())
        mBinding.rv.adapter = mAdapter
        mBinding.btnConnect.setOnClickListener {
            connect()
        }
        mBinding.btnDisconnect.setOnClickListener {
            disconnect()
        }
        mBinding.tvRequestMtu.setOnClickListener {
            requestMtu()
        }
        mBinding.tvReadRemoteRssi.setOnClickListener {
            readRemoteRssi()
        }
        mBinding.tvRequestConnectionPriority.setOnClickListener {
            requestConnectionPriority()
        }
        mBinding.tvConnectStatus.doAfterTextChanged {
            Logger.e("BleConnectFragment", "tvConnectStatus：${it.toString()}")
        }
        return mBinding.root
    }

    private fun connect() {
        val ctx = context ?: return
        val preState = mBinding.tvConnectStatus.text
        mBinding.tvConnectStatus.setTextColor(ContextCompat.getColor(ctx, R.color.ble_text_blue))
        mBinding.tvConnectStatus.text = "连接中……"

        connectExecutor.connect(viewLifecycleOwner.lifecycleScope, autoConnectInterval = 3000, onConnected = {
            mBinding.tvConnectStatus.setTextColor(ContextCompat.getColor(ctx, R.color.ble_text_blue))
            mBinding.tvConnectStatus.text = "连接成功"
            val gattServiceList = connectExecutor.getServices()
            if (!gattServiceList.isNullOrEmpty()) {
                val bleGattServiceInfos = gattServiceList.map { bluetoothGattService ->
                    BleConnectInfo(mData.address, bluetoothGattService)
                }
                mAdapter.submitList(bleGattServiceInfos)
            } else {
                mAdapter.submitList(null)
            }
        }) {
            when (it) {
                is BluetoothExceptionCancelTimeout -> {
                    // 提前取消超时(BleExceptionCancelTimeout)不做处理。因为这是调用 disconnect() 造成的，使用者可以直接在 disconnect() 方法结束后处理 UI 的显示，不需要此回调。
                }

                is BluetoothExceptionBusy -> {
                    mBinding.tvConnectStatus.setTextColor(ContextCompat.getColor(ctx, R.color.ble_text_blue))
                    mBinding.tvConnectStatus.text = preState
                    Toast.makeText(ctx, it.message, Toast.LENGTH_SHORT).show()
                }

                else -> {
                    mBinding.tvConnectStatus.setTextColor(ContextCompat.getColor(ctx, R.color.ble_text_red))
                    mBinding.tvConnectStatus.text = it.message
                    mAdapter.submitList(null)
                    mBinding.etRequestMtu.setText("")
                    mBinding.etReadRemoteRssi.setText("")
                    mBinding.etRequestConnectionPriority.setText("")
                }
            }
        }
    }

    fun disconnect() {
        val ctx = context ?: return
        try {
            connectExecutor.disconnect()
            mBinding.tvConnectStatus.setTextColor(ContextCompat.getColor(ctx, R.color.ble_text_red))
            mBinding.tvConnectStatus.text = "连接断开了"
        } catch (e: BluetoothException) {
            mBinding.tvConnectStatus.setTextColor(ContextCompat.getColor(ctx, R.color.ble_text_red))
            mBinding.tvConnectStatus.text = e.message
        }
    }

    private suspend fun onError(e: Throwable) {
        val ctx = context ?: return
        withContext(Dispatchers.Main) {
            when (e) {
                is BluetoothExceptionCancelTimeout -> {
                    // 提前取消超时(BleExceptionCancelTimeout)不做处理。因为这是调用 disconnect() 造成的，使用者可以直接在 disconnect() 方法结束后处理 UI 的显示，不需要此回调。
                }

                else -> {
                    Toast.makeText(ctx, e.message, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun requestMtu() {
        if (mBinding.etRequestMtu.text.trim().isEmpty()) {
            Toast.makeText(context, "请输入MTU的值", Toast.LENGTH_SHORT).show()
            return
        }
        val mtu = mBinding.etRequestMtu.text.toString().trim().toInt()
        lifecycleScope.launch {
            try {
                connectExecutor.requestMtu(mtu)
                Toast.makeText(context, "设置成功", Toast.LENGTH_SHORT).show()
            } catch (e: BluetoothException) {
                onError(e)
            }
        }
    }

    private fun readRemoteRssi() {
        lifecycleScope.launch {
            try {
                val rssi = connectExecutor.readRemoteRssi(3000)
                mBinding.etReadRemoteRssi.setText(rssi.toString())
            } catch (e: BluetoothException) {
                onError(e)
            }
        }
    }

    private fun requestConnectionPriority() {
        if (mBinding.etRequestConnectionPriority.text.trim().isEmpty()) {
            Toast.makeText(context, "请输入connection priority的值", Toast.LENGTH_SHORT).show()
            return
        }
        val connectionPriority = mBinding.etRequestConnectionPriority.text.toString().trim().toInt()
        lifecycleScope.launch {
            try {
                connectExecutor.requestConnectionPriority(connectionPriority)
                Toast.makeText(context, "设置成功", Toast.LENGTH_SHORT).show()
            } catch (e: BluetoothException) {
                onError(e)
            }
        }
    }

    override fun onDestroy() {
        connectExecutor.close()
        super.onDestroy()
    }

}